/* ccscript lexical analyzer/scanner */
#pragma once

#include <cstdlib>
#include <cstring>
#include <map>
#include <string>
#include <vector>

#include "err.h"

class Lexer;


typedef enum {
	finished = 0,
	identifier, intliteral, stringliteral, constsym, flagsym,
	ifsym, elsesym, menusym, defaultsym, commandsym, andsym, orsym, notsym,
	bytesym, shortsym, longsym, romsym, romtblsym,
	leftparen, rightparen, leftbrace, rightbrace, leftbracket,
	rightbracket, period, colon, comma, equals,
	plussym, minussym, multsym, divsym,
	
	// use for operators not supported by the old parser;
	// this symbol enumeration will just be removed soon
	dummysym,
	
	errorsym
} symbol;


struct Token
{
	// new properties for new parsing approach
	std::string type;		// the type of token
	std::string value;		// token value

	// properties 'sym', 'sval', and 'stype' will be removed.

	symbol	sym;			// the type of token
	int		line;			// the source line on which this token occurs
	int		ival;			// integer value of the token
	std::string	sval;		// string value of the token
	char	stype;			// for strings: indicates type of string literal
	
	std::string	ToString();
};


class Lexer
{
public:
	int line;				// current line being scanned
	int column;				// character column reached

	std::string currenttype;// type of last-lexed token
	symbol currentsym;		// last-lexed token symbol
	int currentint;			// integer value of last-lexed symbol
	std::string currentstr;	// string value of last-lexed symbol
	char currentstype;		// type of string, if last symbol was a string

	enum charcode {
		eob = '\0'
	};

public:
	explicit Lexer(std::string);		// Constructs lexer on given input
	symbol Lex();			// Reads next symbol from input; returns `finished` when there are no more tokens
	symbol Peek();			// Checks the next symbol without advancing or modifying current value.
							//  Just a quick hack to ease parsing of LL(2) grammar :-)

	void GetCurrentToken(Token&) const;	// Gets the last-read token value
	Token GetCurrentToken() const;
	int GetPosition() const;			// Returns the current position within the input
	void SetErrorHandler(ErrorReceiver* e);

	// TODO: eventually, we should just remove "symbol" from the mix.
	// It honestly doesn't perform much of a function. All the available
	// symbols are defined in lexer.h, so why is a symbol a parameter to
	// "AddKeyword", a public method? If we're _adding_ a keyword, why are
	// we restricted to the symbols already defined by the lexer? Circular
	// silliness.
	void AddKeyword(const std::string& kw, symbol sym);
	void AddOperator(const std::string& kw, symbol sym);

	static std::string SymbolToString(symbol sym);

private:
	void Init();
	void Next();
	symbol LexSymbol();
	symbol LexStringLiteral();
	symbol LexIdentifier();
	symbol LexOperator();
	symbol LexNumber();
	void LexSingleComment();
	void LexBlockComment();
	void Error(const std::string& msg);
	void Warning(const std::string& msg);

	ErrorReceiver *error;

	//
	// A simple search tree of sorts used for operator lexing.
	// Since operators aren't always neatly delimited by whitespace
	// (e.g., "+=+" needs to be lexed as "+=","+"), we need to be
	// able to check whether it is possible to reach a valid operator
	// by adding the next character in the stream to the current operator.
	//
	struct optree {
		char value;		// character derivable from this node's parent
		bool complete;	// true if this character concludes a valid operator
		symbol sym;		// 

		std::map<char,optree*> children;
		optree() : value(0), complete(false), sym(errorsym) { }
		optree(char value) : value(value), complete(false), sym(errorsym) { }
		//
		// Add an operator to the tree
		//
		void add(const char* op, symbol s) {
			if(*op == '\0') {
				complete = true;
				sym = s;
				return;
			}
			if(children.find(*op) == children.end())
				children[*op] = new optree(*op);
			children[*op]->add(op + 1, s);
		}
		//
		// Find the node corresponding to the last character of op
		//
		optree* find(const char* op) {
			if(*op == '\0')
				return this;
			else if(children.find(*op) != children.end())
				return children[*op]->find(op + 1);
			else
				return NULL;
		}
		optree* find(const std::string& op) {
			return find(op.c_str());
		}
		//
		// Check if a sequence exists in the tree
		//
		bool contains(const std::string& op) {
			return (find(op.c_str()) != NULL);
		}
		//
		// Check if a given sequence is complete
		//
		bool is_complete(const std::string& op) {
			optree* end = find(op.c_str());
			return end && end->complete;
		}
		~optree() {
			for(std::map<char,optree*>::const_iterator it
				= children.begin(); it != children.end(); ++it)
				delete it->second;
		}
	};

	optree operators;
	std::map<std::string, symbol> keywords;
	std::string in;
	size_t inpos;
	char current;
};

